#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <netinet/in.h>   //struct sockaddr_in结构体的定义以及结构体的宏
#include <arpa/inet.h>   //一些字节序转换的接口
#include <sys/socket.h>  //套接字接口


int main(int argc, char* argv[])
{
    //通过参数传入服务端要绑定的地址信息(IP地址和端口)
    if(argc != 3){
        printf("em: ./udp_srv 192.168.254.120 9000");
        return -1;
    }
    const char *ip_addr = argv[1];  //通过参数获取ip地址
    uint16_t port_addr = atoi(argv[2]); //通过参数获取端口
    
    //创建套接字
    //socket(地址域, 套接字类型, 协议类型)   AF_INET ipv4地址结构  SOCK_DGRAM 数据报套接字  IPPROTO_UDP  UDP协议
    int sockfd = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
    if(sockfd < 0){
        printf(" socket fail!");
        return -1;
    }
    
    //定义IPV4地址信息结构
    struct sockaddr_in addr;   //定义一个ipv4的地址信息结构体,
    socklen_t len = sizeof(struct sockaddr_in);
    addr.sin_family = AF_INET;   //这个成员用于标识这个结构体是什么类型的地址信息结构
    //htons -- 将无符号2个字节的整数转换为网络字节序的数据
    addr.sin_port = htons(port_addr); //网络字节序端口, 使用htons将主机字节序数据转换为网络字节序数据
    //inet_pton -- 将字符串ip地址转换为网络字节序ip地址
    //inet_pton(地址域类型, 字符串ip地址, 接收转换后数据的存放缓冲区)
    inet_pton(AF_INET, ip_addr, &addr.sin_addr.s_addr); // 将一个字符串ip地址转换成网络字节序ip地址

    //绑定地址信息
    int ret;
    //bind(描述符, 要绑定的地址信息, 地址信息长度)
    ret = bind(sockfd, (struct sockaddr*)&addr, len);
    if(ret < 0){
        perror("bind erroe");
        return -1;
    }

    //循环接收或者发送数据
    while(1)
    {
        //作为服务端,应该先接收数据
        //recvfrom(描述符, 缓冲区, 长度, 参数, 对端地址存放缓冲区, 地址长度缓冲区)
        //参数 = 0, 阻塞接收数据,若缓冲区中没有数据,则等待数据的到来
        char buf[1024] = {0}; //用于存放对端发送的数据
        struct sockaddr_in cliaddr; //用于存放对端地址信息
        len = sizeof(struct sockaddr_in); //用于存放对端地址信息的长度
        ret = recvfrom(sockfd, buf, 1023, 0, (struct sockaddr*)&cliaddr, &len); //阻塞接收数据
        if(ret < 0){
            perror("recvfrom error");
            return -1;
        }
        char cli_ip[32] = {0}; //用于存放对端ip地址信息
        inet_ntop(AF_INET, &cliaddr.sin_addr, cli_ip, 32); //将接收到的网络字节序信息转换为本机字节序信息
        uint16_t cli_port = ntohs(cliaddr.sin_port); //用于存放对端端口信息,并将网络字节序端口信息转换为本机字节序信息
        printf("client[%s][%d] say: %s\n", cli_ip, cli_port, buf);  //将接收到的数据打印
        
        printf("server say: ");
        fflush(stdout); //上面的打印没有 /n 所以需要手动刷新输出缓冲区
        memset(buf, 0x00, 1024);  //初始化buf缓冲区为0   接收到的信息已使用,则将buf清空
        scanf("%s",buf); //从标准输入获取数据并存放到buf中,   在此之前buf已被清空
        //sendto(描述符,要发送的数据, 数据长度, 参数, 对端地址, 地址长度)
        //参数 = 0 -- 标识阻塞发送,数据放不到缓冲区(缓冲区满了), 则等待
        ret = sendto(sockfd, buf, strlen(buf), 0, (struct sockaddr*)&cliaddr, len); //发送信息
        if(ret < 0){
            perror("sendto error");
            close(sockfd);
            return -1;
        }
        
    }
    return 0;
}
